Read in MoTR Data
# Interesting way of reading multiple files in one directory!
file_prefix = "../reading_measures/cleaned_f160"
fnames = list.files(path=file_prefix)
# fnames: [1] "reader_55_reading_measures.csv" "reader_56_reading_measures.csv"
# [3] "reader_59_reading_measures.csv" "reader_61_reading_measures.csv"
# Read in the data
df = data.frame()
# uses the mutate() function from the dplyr package to create a new column in a data frame called subj. The new column is created by removing the substring _reading_measures.csv from the values in an existing column called f.
for (f in fnames) {
temp = read.csv(paste0(file_prefix, "/", f)) %>%
mutate(subj = str_remove(f, "_reading_measures.csv")) %>%
dplyr::select(expr_id, cond_id, para_nr, word, word_nr, first_duration, total_duration,
gaze_duration, go_pass_time, FPReg, subj) %>%
rename(go_past_time = go_pass_time)
df = rbind(df, temp)
}
df
# `gather will take column 6-9's column name make it rows, name 'metric', take their value as another row`
motr_df = df %>%
mutate(expr_id = if_else(expr_id == 1, "Attachment", "Provo")) %>%
gather(metric, value, 6:10)
motr_df
NA
# Average across subjects
motr_agg_df = motr_df %>%
drop_na() %>%
group_by(expr_id, cond_id, para_nr, word, word_nr, metric) %>%
summarise(value = mean(value)) %>% # sum up four subjects for each metric and divide by 4.
ungroup() %>%
arrange(expr_id, cond_id, para_nr, word_nr) # like sort in python
`summarise()` has grouped output by 'expr_id', 'cond_id', 'para_nr', 'word', 'word_nr'. You can override using the `.groups` argument.
# View(motr_agg_df)
motr_agg_df
table(motr_df$subj)
reader_55 reader_56 reader_59 reader_61 reader_62 reader_63
3100 3100 3100 3100 3100 3100
# reader_55 reader_56 reader_59 reader_61
# 3100 3100 3100 3100
motr_provo_df = motr_agg_df %>%
filter(expr_id == "Provo") %>%
rename(text_id = para_nr,
word_text_idx = word_nr,
motr_value = value) %>%
dplyr::select(-expr_id, -cond_id)
motr_provo_df
NA
Comparison to Provo
# Read in Provo surprisal, frequency and length data
provo_modeling_df = read.csv("../ancillary_data/provo_df.csv") %>%
dplyr::select(text_id, sent_id, trigger_idx, word, freq, surp, len) %>%
rename(word_idx = trigger_idx)
provo_modeling_df
NA
# Read in Provo eyetracking data
provo_raw_df = read.csv("../ancillary_data/provo_eyetracking.csv")
provo_eyetracking_df = provo_raw_df %>%
dplyr::select(Participant_ID, Text_ID, Sentence_Number, Word_In_Sentence_Number, IA_ID, Word,IA_FIRST_FIXATION_DURATION, IA_FIRST_FIX_PROGRESSIVE, IA_FIRST_RUN_DWELL_TIME, IA_DWELL_TIME, IA_REGRESSION_PATH_DURATION, IA_REGRESSION_OUT) %>%
rename( first_duration = IA_FIRST_FIXATION_DURATION, # whether it is first pass?
gaze_duration = IA_FIRST_RUN_DWELL_TIME,
total_duration = IA_DWELL_TIME,
go_past_time = IA_REGRESSION_PATH_DURATION,
FPReg = IA_REGRESSION_OUT,
subj = Participant_ID,
text_id = Text_ID,
sent_id = Sentence_Number,
word_idx = Word_In_Sentence_Number,
word_text_idx = IA_ID,
word = Word,
ff_progressive = IA_FIRST_FIX_PROGRESSIVE) %>% # notice:average across subj, binary(0,1) becomes float.
mutate(gaze_duration = ifelse(ff_progressive == 0, 0, gaze_duration),
go_past_time = ifelse(ff_progressive == 0, 0, go_past_time)) %>%
dplyr::select(-ff_progressive) %>%
gather(metric, value, 7:11) %>% # not include FFReg which is in column 11
mutate(value = if_else(is.na(value), as.integer(0), as.integer(value))) %>%
drop_na() %>% # actually, drop first word in a sentence
group_by(text_id, word_text_idx, sent_id, word_idx, word, metric) %>%
summarise(value = mean(value)) %>%
ungroup()
`summarise()` has grouped output by 'text_id', 'word_text_idx', 'sent_id', 'word_idx', 'word'. You can override using the `.groups` argument.
# View(provo_eyetracking_df)
provo_eyetracking_df
NA
provo_df = merge(provo_eyetracking_df, provo_modeling_df, by=c("text_id", "sent_id", "word_idx")) %>%
mutate(word_text_idx = as.integer(word_text_idx - 1)) %>%
arrange(text_id, sent_id, word_idx)
provo_df
provo_df = merge(provo_df, motr_provo_df, by=c("text_id", "word_text_idx", "metric")) %>%
rename(eyetr_value = value) %>%
arrange(text_id, sent_id, word_idx) %>%
filter(word.x == word) %>% #word.y has no captical word
dplyr::select(-word.x, -word.y) %>%
mutate(motr_outlier = if_else(motr_value > (mean(motr_value) + 3 * sd(motr_value) ), T, F)) %>%
filter(motr_outlier == F) %>% # clear outlier -> 13 was filtered.
gather(measure, value, c("eyetr_value", "motr_value")) %>%
# filter(metric != "first_duration") %>%
filter(metric != "FPReg")
# View(provo_df)
provo_df %>%
ggplot(aes(x = value, color=metric)) +
geom_density() +
facet_wrap(.~measure) +
xlab("Reading Measure")

# ggsave("../visualization/density.png", device = "png", width = 6, height = 2.5)
provo_df %>%
filter(measure == "motr_value") %>%
ggplot(aes(x = value, color=metric)) +
geom_density() +
xlab("Reading Measure") +
ggtitle("Density plot of value for motr")

provo_df %>%
filter(measure == "eyetr_value") %>%
ggplot(aes(x = value, color=metric)) +
geom_density() +
xlab("Reading Measure") +
ggtitle("Density plot of value for eyetr")

gd_df = provo_df %>% filter(metric == "total_duration") %>% spread(measure, value)
View(gd_df)
cor.test(gd_df$eyetr_value, gd_df$motr_value) #cor rise from 0.529 to 0.5989
Pearson's product-moment correlation
data: gd_df$eyetr_value and gd_df$motr_value
t = 18, df = 377, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.6113 0.7226
sample estimates:
cor
0.6707
provo_df %>%
spread(measure, value) %>%
ggplot(aes(x = motr_value, y=eyetr_value, color=metric)) +
geom_point(alpha = 0.2) +
facet_wrap(.~metric, scales="free") +
geom_smooth()

# ggsave("../visualization/metric_cor.png", device = "png", width = 6, height = 2.5)
gd_df_2 = provo_df %>% filter(metric == "first_duration") %>% spread(measure, value)
cor.test(gd_df_2$eyetr_value, gd_df_2$motr_value)
Pearson's product-moment correlation
data: gd_df_2$eyetr_value and gd_df_2$motr_value
t = 18, df = 379, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.6220 0.7305
sample estimates:
cor
0.68
gd_df_3 = provo_df %>% filter(metric == "gaze_duration") %>% spread(measure, value)
cor.test(gd_df_3$eyetr_value, gd_df_3$motr_value)
Pearson's product-moment correlation
data: gd_df_3$eyetr_value and gd_df_3$motr_value
t = 19, df = 379, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.6406 0.7447
sample estimates:
cor
0.6963
gd_df_4 = provo_df %>% filter(metric == "go_past_time") %>% spread(measure, value)
cor.test(gd_df_4$eyetr_value, gd_df_4$motr_value)
Pearson's product-moment correlation
data: gd_df_4$eyetr_value and gd_df_4$motr_value
t = 14, df = 368, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.5197 0.6530
sample estimates:
cor
0.5903
provo_df %>%
gather(word_prop, word_prop_val, c("freq", "len", "surp")) %>%
filter(metric == "gaze_duration") %>%
ggplot(aes(x = value, y=word_prop_val, color = measure)) +
geom_point(alpha = 0.2) +
facet_grid(word_prop~measure, scales="free") +
geom_smooth() +
xlab("Reading Measure")

# ggsave("../visualization/word_prop_comps.png", device = "png", width = 6, height = 3)
provo_df %>%
ggplot(aes(x = value, y=freq, color=metric)) +
geom_point(alpha = 0.2) +
facet_grid(metric~measure, scales="free") +
geom_smooth()

provo_df %>%
ggplot(aes(x = value, y=surp, color=metric)) +
geom_point(alpha = 0.2) +
facet_grid(metric~measure, scales="free") +
geom_smooth()

Shape of surprisal / RT relationship
fit_gam_inner = function(bootstrap_sample, mean_predictors) {
df = bootstrap_sample$data
weights = tabulate(as.integer(bootstrap_sample), nrow(df))
m = gam(psychometric ~ s(surp, bs = 'cr', k = 6) + s(prev_surp, bs = 'cr', k = 6) + te(freq, len, bs = 'cr') + te(prev_freq, prev_len, bs = 'cr'), data = df, weights = weights)
terms_to_predict = c("s(surp)", "s(prev_surp)")
newdata = data.frame(surp=seq(0,20,by=0.1), prev_surp=mean_predictors$surp,
freq=mean_predictors$freq, prev_freq=mean_predictors$freq,
len=mean_predictors$freq, prev_len=mean_predictors$freq)
# Returns a matrix N_samples * N_terms.
per_term_predictions = predict(m, newdata=newdata, terms=terms_to_predict, type="terms")
# Additive model -- sum across predictor response contributions (matrix columns).
predictions = rowSums(per_term_predictions)
return(newdata %>% mutate(y=predictions))
}
fit_gam = function(df, mean_predictors, alpha=0.05) {
# Bootstrap-resample data
boot_models = df %>% bootstraps(times=10) %>%
# Fit a GAM and get predictions for each sample
mutate(smoothed=map(splits, fit_gam_inner, mean_predictors=mean_predictors))
# Extract mean and 5% and 95% percentile y-values for each surprisal value
result = boot_models %>%
unnest(smoothed) %>%
dplyr::select(surp, y) %>%
group_by(surp) %>%
summarise(y_lower=quantile(y, alpha / 2),
y_upper=quantile(y, 1 - alpha / 2),
y=mean(y)) %>%
ungroup()
return (result)
}
gam_modeling_df = provo_df %>%
spread(measure, value) %>%
mutate(len = nchar(word)) %>%
group_by(metric, text_id) %>%
arrange(word_text_idx) %>%
mutate(prev_surp = lag(surp),
prev_freq = lag(freq),
prev_len = lag(len),
prev_eyetr_value = lag(eyetr_value)) %>%
ungroup() %>%
drop_na() %>%
rename(psychometric = motr_value)
smooths_df = data.frame()
metrics = c("gaze_duration", "total_duration", "go_past_time", "first_duration")
for (m in metrics) {
print(paste0("Fitting model for ", m))
dummy_df = gam_modeling_df %>% filter(metric == m)
mean_predictors = dummy_df %>% summarise(surp = mean(surp), len = mean(len), freq = mean(freq))
smooths = dummy_df %>% fit_gam(., mean_predictors)
#Fix 0 surprisal = 0 ms
gam_smooths = smooths %>% mutate(delta = 0 - y[1], y=y + delta, y_lower= y_lower + delta, y_upper=y_upper + delta)
smooths_df = rbind(smooths_df, gam_smooths %>% mutate(psychometric = m))
}
[1] "Fitting model for gaze_duration"
[1] "Fitting model for total_duration"
[1] "Fitting model for go_past_time"
[1] "Fitting model for first_duration"
# Surprisal curves
ggplot() +
geom_line(data = smooths_df, aes(x=surp, y=y, color = psychometric), size=0.7) +
geom_ribbon(data = smooths_df, aes(x=surp, ymin=y_lower, ymax=y_upper, fill = psychometric), alpha=0.3, size=0.5) +
scale_x_continuous(labels=c(0, 10, 20), breaks=c(0, 10, 20), minor_breaks = NULL) +
facet_wrap(psychometric~.) +
ylab("Slowdown due to Surprisal (ms)") +
xlab("Surprisal of Word") +
ggtitle("Relationship between MoTR Times and Surprisal")
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
Please use `linewidth` instead.

theme(
legend.position = "none",
panel.grid.minor = element_blank()
)
List of 2
$ legend.position : chr "none"
$ panel.grid.minor: list()
..- attr(*, "class")= chr [1:2] "element_blank" "element"
- attr(*, "class")= chr [1:2] "theme" "gg"
- attr(*, "complete")= logi FALSE
- attr(*, "validate")= logi TRUE
Targeted Evaluation Data
motr_attach_df = motr_df %>%
filter(expr_id == "Attachment") %>%
rename( item_id = para_nr) %>%
mutate(item_id = as.integer(item_id)) %>%
mutate(cond_id = as.factor(cond_id)) %>%
mutate(cond_id = if_else(cond_id == 1, "No Comma", "Comma")) %>%
filter(metric != "FPReg") %>%
filter(! (item_id == 4 & cond_id == "No Comma") ) %>% # just because of alignment issues for now
mutate(crit = if_else(word == "and", word_nr, as.integer(0) )) %>%
group_by(cond_id, item_id) %>%
mutate(crit = unique(crit)[2]) %>%
ungroup() %>%
mutate(word_nr = word_nr - crit)
agg_motr_attach_df = motr_attach_df %>%
filter(word_nr >= -2, word_nr < 6) %>%
group_by(cond_id, word_nr, metric) %>%
summarise( m = mean(value),
sd = std.error(value),
upper = m + 1.96 * sd,
lower = m - 1.98 * sd,
n = n()) %>%
ungroup()
`summarise()` has grouped output by 'cond_id', 'word_nr'. You can override using the `.groups` argument.
agg_motr_attach_df %>%
ggplot(aes(x = word_nr, y = m, color = cond_id)) +
geom_rect(aes(xmin = 2.5, xmax = 5.5, ymin = 100, ymax = 800), fill=alpha("white", 0), color = "#45ef70", linetype = "dotted") +
geom_point() +
geom_errorbar(aes(ymax = upper, ymin = lower), width = 0.3) +
geom_line() +
#geom_text(aes(label = word, y = if_else(cond_id == "Comma", 3000, 3500)), size = 2) +
#facet_grid(para_nr~cond_id) +
ylab("Reading Time") +
xlab("Condition") +
scale_x_continuous(breaks=-2:5, labels=c("the", "man", "and", "his", "wife", "ran", "away", "from")) +
facet_grid(~metric) +
theme(
legend.position = "bottom",
axis.text.x = element_text(angle = 45, hjust = 1)
)

#ggsave("../visualization/attachment.png", device = "png", width = 6, height = 3)
options(JULIA_HOME = "/Applications/Julia-1.8.app/Contents/Resources/julia/bin/")
# library(jglmm)
# jglmm_setup()
attach_lm_df = motr_attach_df %>%
filter(metric == "gaze_duration") %>%
filter(word_nr == 3) %>%
mutate(item_id = as.factor(item_id),
subj = as.factor(subj))
m = attach_lm_df %>%
lmer(value ~ cond_id + (cond_id | item_id) + (cond_id | subj), data=.)
boundary (singular) fit: see help('isSingular')
summary(m)
Linear mixed model fit by REML. t-tests use Satterthwaite's method ['lmerModLmerTest']
Formula: value ~ cond_id + (cond_id | item_id) + (cond_id | subj)
Data: .
REML criterion at convergence: 1241
Scaled residuals:
Min 1Q Median 3Q Max
-1.287 -0.803 -0.253 0.738 4.223
Random effects:
Groups Name Variance Std.Dev. Corr
item_id (Intercept) 2577 50.8
cond_idNo Comma 689 26.3 -0.08
subj (Intercept) 2296 47.9
cond_idNo Comma 8756 93.6 1.00
Residual 64356 253.7
Number of obs: 90, groups: item_id, 15; subj, 6
Fixed effects:
Estimate Std. Error df t value Pr(>|t|)
(Intercept) 239.87 45.23 5.98 5.30 0.0018 **
cond_idNo Comma 75.63 71.36 7.01 1.06 0.3244
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Correlation of Fixed Effects:
(Intr)
cond_idNCmm -0.284
optimizer (nloptwrap) convergence code: 0 (OK)
boundary (singular) fit: see help('isSingular')
LS0tCnRpdGxlOiAiRXhwbG9yYXRvcnkgQW5hbHlzaXMgZm9yIE1vVFIgUmVhZGluZyBEYXRhIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0KIyBgc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzYCBpcyBhIGZ1bmN0aW9uIGluIFIgdGhhdCBzdXBwcmVzc2VzIHRoZSBzdGFydHVwIG1lc3NhZ2VzIHRoYXQgYXJlIGdlbmVyYXRlZCB3aGVuIGxvYWRpbmcgYSBwYWNrYWdlLiBJdCBjYW4gYmUgdXNlZnVsIHRvIHByZXZlbnQgdGhlIGNvbnNvbGUgZnJvbSBiZWluZyBjbHV0dGVyZWQgd2l0aCBtZXNzYWdlcyB3aGVuIHlvdSBhcmUgbG9hZGluZyBtdWx0aXBsZSBwYWNrYWdlcyBvciB3aGVuIHlvdSBkb24ndCB3YW50IHRvIHNlZSB0aGUgcGFja2FnZSBsb2FkaW5nIG1lc3NhZ2VzLgpzaGhoIDwtIHN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyAjIEl0J3MgYSBsaWJyYXJ5LCBzbyBzaGhoIQoKCnNoaGgobGlicmFyeSggbWdjdiApKQpzaGhoKGxpYnJhcnkoZHBseXIpKQpzaGhoKGxpYnJhcnkoZ2dwbG90MikpCnNoaGgobGlicmFyeShsbWU0KSkKc2hoaChsaWJyYXJ5KHRpZHltdikpCnNoaGgobGlicmFyeShnYW1sc3MpKQpzaGhoKGxpYnJhcnkoZ3N1YmZuKSkKc2hoaChsaWJyYXJ5KGxtZXJUZXN0KSkKc2hoaChsaWJyYXJ5KHRpZHl2ZXJzZSkpCnNoaGgobGlicmFyeShib290KSkKc2hoaChsaWJyYXJ5KHJzYW1wbGUpKQpzaGhoKGxpYnJhcnkocGxvdHJpeCkpCnNoaGgobGlicmFyeShnZ3JlcGVsKSkKc2hoaChsaWJyYXJ5KG1nY3YpKQoKIyBgdGhlbWVfc2V0KHRoZW1lX2J3KCkpYCBpcyBhIGNvbW1hbmQgaW4gUiB0aGF0IHNldHMgdGhlIGRlZmF1bHQgdGhlbWUgZm9yIGFsbCBzdWJzZXF1ZW50IHBsb3RzIHRvIGEgdGhlbWUgY2FsbGVkIGB0aGVtZV9idygpYC4KIyBgdGhlbWVfYncoKWAgaXMgYSBidWlsdC1pbiB0aGVtZSBpbiB0aGUgZ2dwbG90MiBwYWNrYWdlIHRoYXQgcHJvdmlkZXMgYSBjbGFzc2ljIGJsYWNrLWFuZC13aGl0ZSB0aGVtZSB3aXRoIHdoaXRlIGdyaWQgbGluZXMuIEJ5IHNldHRpbmcgdGhlIGRlZmF1bHQgdGhlbWUgdG8gYHRoZW1lX2J3KClgLCBhbGwgcGxvdHMgdGhhdCBhcmUgY3JlYXRlZCB1c2luZyBnZ3Bsb3QyIHdpbGwgaGF2ZSB0aGlzIHRoZW1lIHVubGVzcyBhIGRpZmZlcmVudCB0aGVtZSBpcyBleHBsaWNpdGx5IHNwZWNpZmllZC4KdGhlbWVfc2V0KHRoZW1lX2J3KCkpCgojIGBvcHRpb25zKGRpZ2l0cz00KWAgaXMgYSBjb21tYW5kIGluIFIgdGhhdCBzZXRzIHRoZSBudW1iZXIgb2YgZGlnaXRzIHRvIGRpc3BsYXkgd2hlbiBwcmludGluZyBudW1lcmljIHZhbHVlcy4KCiMgQnkgZGVmYXVsdCwgUiB3aWxsIHByaW50IHVwIHRvIDcgZGlnaXRzIGZvciBudW1lcmljIHZhbHVlcy4gSG93ZXZlciwgeW91IGNhbiB1c2UgdGhlIGBvcHRpb25zKClgIGZ1bmN0aW9uIHRvIGNoYW5nZSB0aGlzIGJlaGF2aW9yLiBJbiB0aGlzIGNhc2UsIGBvcHRpb25zKGRpZ2l0cz00KWAgc2V0cyB0aGUgbnVtYmVyIG9mIGRpZ2l0cyB0byA0LCB3aGljaCBtZWFucyB0aGF0IGFsbCBudW1lcmljIHZhbHVlcyB3aWxsIGJlIGRpc3BsYXllZCB3aXRoIGF0IG1vc3QgNCBkaWdpdHMuCm9wdGlvbnMoZGlnaXRzPTQpCnNldC5zZWVkKDQ0NCkKCiMgVGhlIGBwaXBlX21lc3NhZ2VgIGZ1bmN0aW9uIGZpcnN0IG91dHB1dHMgdGhlIHN0YXR1cyBtZXNzYWdlIHVzaW5nIHRoZSBtZXNzYWdlKCkgZnVuY3Rpb24uIFRoaXMgY2FuIGJlIHVzZWZ1bCBmb3IgdHJhY2tpbmcgdGhlIHByb2dyZXNzIG9mIGEgbG9uZyBjb21wdXRhdGlvbiBvciBmb3IgcHJvdmlkaW5nIGluZm9ybWF0aW9uIHRvIHRoZSB1c2VyLgojIEFmdGVyIHByaW50aW5nIHRoZSBtZXNzYWdlLCB0aGUgZnVuY3Rpb24gcmV0dXJucyB0aGUgLmRhdGEgYXJndW1lbnQgdW5jaGFuZ2VkLiBUaGlzIGFsbG93cyB0aGUgZnVuY3Rpb24gdG8gYmUgdXNlZCBhcyBhICJwaXBlIiBpbiBhIGRhdGEgcHJvY2Vzc2luZyBwaXBlbGluZSwgd2hlcmUgdGhlIG91dHB1dCBvZiBvbmUgZnVuY3Rpb24gaXMgdXNlZCBhcyB0aGUgaW5wdXQgdG8gdGhlIG5leHQgZnVuY3Rpb24uCnBpcGVfbWVzc2FnZSA9IGZ1bmN0aW9uKC5kYXRhLCBzdGF0dXMpIHttZXNzYWdlKHN0YXR1cyk7IC5kYXRhfQoKYGBgCgpgYGB7cn0KCmBgYAoKIyBSZWFkIGluIE1vVFIgRGF0YQoKYGBge3J9CiMgSW50ZXJlc3Rpbmcgd2F5IG9mIHJlYWRpbmcgbXVsdGlwbGUgZmlsZXMgaW4gb25lIGRpcmVjdG9yeSEKCmZpbGVfcHJlZml4ID0gIi4uL3JlYWRpbmdfbWVhc3VyZXMvY2xlYW5lZF9mMTYwIgpmbmFtZXMgPSBsaXN0LmZpbGVzKHBhdGg9ZmlsZV9wcmVmaXgpCiMgZm5hbWVzOiBbMV0gInJlYWRlcl81NV9yZWFkaW5nX21lYXN1cmVzLmNzdiIgInJlYWRlcl81Nl9yZWFkaW5nX21lYXN1cmVzLmNzdiIKIyBbM10gInJlYWRlcl81OV9yZWFkaW5nX21lYXN1cmVzLmNzdiIgInJlYWRlcl82MV9yZWFkaW5nX21lYXN1cmVzLmNzdiIKCiMgUmVhZCBpbiB0aGUgZGF0YQpkZiA9IGRhdGEuZnJhbWUoKQoKIyB1c2VzIHRoZSBtdXRhdGUoKSBmdW5jdGlvbiBmcm9tIHRoZSBkcGx5ciBwYWNrYWdlIHRvIGNyZWF0ZSBhIG5ldyBjb2x1bW4gaW4gYSBkYXRhIGZyYW1lIGNhbGxlZCBzdWJqLiBUaGUgbmV3IGNvbHVtbiBpcyBjcmVhdGVkIGJ5IHJlbW92aW5nIHRoZSBzdWJzdHJpbmcgX3JlYWRpbmdfbWVhc3VyZXMuY3N2IGZyb20gdGhlIHZhbHVlcyBpbiBhbiBleGlzdGluZyBjb2x1bW4gY2FsbGVkIGYuCmZvciAoZiBpbiBmbmFtZXMpIHsKICB0ZW1wID0gcmVhZC5jc3YocGFzdGUwKGZpbGVfcHJlZml4LCAiLyIsIGYpKSAlPiUKICAgIG11dGF0ZShzdWJqID0gc3RyX3JlbW92ZShmLCAiX3JlYWRpbmdfbWVhc3VyZXMuY3N2IikpICU+JQogICAgZHBseXI6OnNlbGVjdChleHByX2lkLCBjb25kX2lkLCBwYXJhX25yLCB3b3JkLCB3b3JkX25yLCBmaXJzdF9kdXJhdGlvbiwgdG90YWxfZHVyYXRpb24sCiAgICAgICAgICAgICAgICAgIGdhemVfZHVyYXRpb24sIGdvX3Bhc3NfdGltZSwgRlBSZWcsIHN1YmopICU+JQogICAgcmVuYW1lKGdvX3Bhc3RfdGltZSA9IGdvX3Bhc3NfdGltZSkKICBkZiA9IHJiaW5kKGRmLCB0ZW1wKQp9CmRmCgojIGBnYXRoZXIgd2lsbCB0YWtlIGNvbHVtbiA2LTkncyBjb2x1bW4gbmFtZSBtYWtlIGl0IHJvd3MsIG5hbWUgJ21ldHJpYycsIHRha2UgdGhlaXIgdmFsdWUgYXMgYW5vdGhlciByb3dgCm1vdHJfZGYgPSBkZiAlPiUKICBtdXRhdGUoZXhwcl9pZCA9IGlmX2Vsc2UoZXhwcl9pZCA9PSAxLCAiQXR0YWNobWVudCIsICJQcm92byIpKSAlPiUKICBnYXRoZXIobWV0cmljLCB2YWx1ZSwgNjoxMCkKCm1vdHJfZGYKCmBgYAoKYGBge3J9CiMgQXZlcmFnZSBhY3Jvc3Mgc3ViamVjdHMKbW90cl9hZ2dfZGYgPSBtb3RyX2RmICU+JQogIGRyb3BfbmEoKSAlPiUKICBncm91cF9ieShleHByX2lkLCBjb25kX2lkLCBwYXJhX25yLCB3b3JkLCB3b3JkX25yLCBtZXRyaWMpICU+JQogICAgc3VtbWFyaXNlKHZhbHVlID0gbWVhbih2YWx1ZSkpICU+JSAgICAjIHN1bSB1cCBmb3VyIHN1YmplY3RzIGZvciBlYWNoIG1ldHJpYyBhbmQgZGl2aWRlIGJ5IDQuCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UoZXhwcl9pZCwgY29uZF9pZCwgcGFyYV9uciwgd29yZF9ucikgICAjIGxpa2Ugc29ydCBpbiBweXRob24KCiMgVmlldyhtb3RyX2FnZ19kZikKCm1vdHJfYWdnX2RmCgp0YWJsZShtb3RyX2RmJHN1YmopCiMgcmVhZGVyXzU1IHJlYWRlcl81NiByZWFkZXJfNTkgcmVhZGVyXzYxIAojICAgIDMxMDAgICAgICAzMTAwICAgICAgMzEwMCAgICAgIDMxMDAKCmBgYAoKYGBge3J9Cgptb3RyX3Byb3ZvX2RmID0gbW90cl9hZ2dfZGYgJT4lCiAgZmlsdGVyKGV4cHJfaWQgPT0gIlByb3ZvIikgJT4lCiAgcmVuYW1lKHRleHRfaWQgPSBwYXJhX25yLAogICAgICAgICB3b3JkX3RleHRfaWR4ID0gd29yZF9uciwKICAgICAgICAgbW90cl92YWx1ZSA9IHZhbHVlKSAlPiUKICBkcGx5cjo6c2VsZWN0KC1leHByX2lkLCAtY29uZF9pZCkKCm1vdHJfcHJvdm9fZGYKCmBgYAoKCgojIENvbXBhcmlzb24gdG8gUHJvdm8KCmBgYHtyfQojIFJlYWQgaW4gUHJvdm8gc3VycHJpc2FsLCBmcmVxdWVuY3kgYW5kIGxlbmd0aCBkYXRhCnByb3ZvX21vZGVsaW5nX2RmID0gcmVhZC5jc3YoIi4uL2FuY2lsbGFyeV9kYXRhL3Byb3ZvX2RmLmNzdiIpICU+JQogIGRwbHlyOjpzZWxlY3QodGV4dF9pZCwgc2VudF9pZCwgdHJpZ2dlcl9pZHgsIHdvcmQsIGZyZXEsIHN1cnAsIGxlbikgJT4lCiAgcmVuYW1lKHdvcmRfaWR4ID0gdHJpZ2dlcl9pZHgpCgpwcm92b19tb2RlbGluZ19kZgoKYGBgCgpgYGB7cn0KIyBSZWFkIGluIFByb3ZvIGV5ZXRyYWNraW5nIGRhdGEKCnByb3ZvX3Jhd19kZiA9IHJlYWQuY3N2KCIuLi9hbmNpbGxhcnlfZGF0YS9wcm92b19leWV0cmFja2luZy5jc3YiKQoKcHJvdm9fZXlldHJhY2tpbmdfZGYgPSBwcm92b19yYXdfZGYgJT4lCiAgZHBseXI6OnNlbGVjdChQYXJ0aWNpcGFudF9JRCwgVGV4dF9JRCwgU2VudGVuY2VfTnVtYmVyLCBXb3JkX0luX1NlbnRlbmNlX051bWJlciwgSUFfSUQsIFdvcmQsSUFfRklSU1RfRklYQVRJT05fRFVSQVRJT04sIElBX0ZJUlNUX0ZJWF9QUk9HUkVTU0lWRSwgSUFfRklSU1RfUlVOX0RXRUxMX1RJTUUsIElBX0RXRUxMX1RJTUUsIElBX1JFR1JFU1NJT05fUEFUSF9EVVJBVElPTiwgSUFfUkVHUkVTU0lPTl9PVVQpICU+JQogIHJlbmFtZSggZmlyc3RfZHVyYXRpb24gPSBJQV9GSVJTVF9GSVhBVElPTl9EVVJBVElPTiwgICAgIyB3aGV0aGVyIGl0IGlzIGZpcnN0IHBhc3M/CiAgICAgICAgICBnYXplX2R1cmF0aW9uID0gSUFfRklSU1RfUlVOX0RXRUxMX1RJTUUsCiAgICAgICAgICB0b3RhbF9kdXJhdGlvbiA9IElBX0RXRUxMX1RJTUUsCiAgICAgICAgICBnb19wYXN0X3RpbWUgPSBJQV9SRUdSRVNTSU9OX1BBVEhfRFVSQVRJT04sCiAgICAgICAgICBGUFJlZyA9IElBX1JFR1JFU1NJT05fT1VULAogICAgICAgICAgc3ViaiA9IFBhcnRpY2lwYW50X0lELAogICAgICAgICAgdGV4dF9pZCA9IFRleHRfSUQsCiAgICAgICAgICBzZW50X2lkID0gU2VudGVuY2VfTnVtYmVyLAogICAgICAgICAgd29yZF9pZHggPSBXb3JkX0luX1NlbnRlbmNlX051bWJlciwKICAgICAgICAgIHdvcmRfdGV4dF9pZHggPSBJQV9JRCwKICAgICAgICAgIHdvcmQgPSBXb3JkLAogICAgICAgICAgZmZfcHJvZ3Jlc3NpdmUgPSBJQV9GSVJTVF9GSVhfUFJPR1JFU1NJVkUpICU+JSAjIG5vdGljZTphdmVyYWdlIGFjcm9zcyBzdWJqLCBiaW5hcnkoMCwxKSBiZWNvbWVzIGZsb2F0LgogIG11dGF0ZShnYXplX2R1cmF0aW9uID0gaWZlbHNlKGZmX3Byb2dyZXNzaXZlID09IDAsIDAsIGdhemVfZHVyYXRpb24pLAogICAgICAgICBnb19wYXN0X3RpbWUgPSBpZmVsc2UoZmZfcHJvZ3Jlc3NpdmUgPT0gMCwgMCwgZ29fcGFzdF90aW1lKSkgJT4lCiAgZHBseXI6OnNlbGVjdCgtZmZfcHJvZ3Jlc3NpdmUpICU+JQogIGdhdGhlcihtZXRyaWMsIHZhbHVlLCA3OjExKSAlPiUgICAgICAjIG5vdCBpbmNsdWRlIEZGUmVnIHdoaWNoIGlzIGluIGNvbHVtbiAxMQogIG11dGF0ZSh2YWx1ZSA9IGlmX2Vsc2UoaXMubmEodmFsdWUpLCBhcy5pbnRlZ2VyKDApLCBhcy5pbnRlZ2VyKHZhbHVlKSkpICU+JQogIGRyb3BfbmEoKSAlPiUgICAjIGFjdHVhbGx5LCBkcm9wIGZpcnN0IHdvcmQgaW4gYSBzZW50ZW5jZQogIGdyb3VwX2J5KHRleHRfaWQsIHdvcmRfdGV4dF9pZHgsIHNlbnRfaWQsIHdvcmRfaWR4LCB3b3JkLCBtZXRyaWMpICU+JQogIHN1bW1hcmlzZSh2YWx1ZSA9IG1lYW4odmFsdWUpKSAlPiUKICB1bmdyb3VwKCkKCiMgVmlldyhwcm92b19leWV0cmFja2luZ19kZikKcHJvdm9fZXlldHJhY2tpbmdfZGYKCmBgYAoKCmBgYHtyfQpwcm92b19kZiA9IG1lcmdlKHByb3ZvX2V5ZXRyYWNraW5nX2RmLCBwcm92b19tb2RlbGluZ19kZiwgYnk9YygidGV4dF9pZCIsICJzZW50X2lkIiwgIndvcmRfaWR4IikpICU+JQogIG11dGF0ZSh3b3JkX3RleHRfaWR4ID0gYXMuaW50ZWdlcih3b3JkX3RleHRfaWR4IC0gMSkpICU+JQogIGFycmFuZ2UodGV4dF9pZCwgc2VudF9pZCwgd29yZF9pZHgpCnByb3ZvX2RmCgpwcm92b19kZiA9IG1lcmdlKHByb3ZvX2RmLCBtb3RyX3Byb3ZvX2RmLCBieT1jKCJ0ZXh0X2lkIiwgIndvcmRfdGV4dF9pZHgiLCAibWV0cmljIikpICU+JQogIHJlbmFtZShleWV0cl92YWx1ZSA9IHZhbHVlKSAlPiUKICBhcnJhbmdlKHRleHRfaWQsIHNlbnRfaWQsIHdvcmRfaWR4KSAlPiUKICBmaWx0ZXIod29yZC54ID09IHdvcmQpICU+JSAgICAgICN3b3JkLnkgaGFzIG5vIGNhcHRpY2FsIHdvcmQKICBkcGx5cjo6c2VsZWN0KC13b3JkLngsIC13b3JkLnkpICU+JQogIG11dGF0ZShtb3RyX291dGxpZXIgPSBpZl9lbHNlKG1vdHJfdmFsdWUgPiAobWVhbihtb3RyX3ZhbHVlKSArIDMgKiBzZChtb3RyX3ZhbHVlKSApLCBULCBGKSkgJT4lCiAgZmlsdGVyKG1vdHJfb3V0bGllciA9PSBGKSAlPiUgICAgICMgY2xlYXIgb3V0bGllciAtPiAxMyB3YXMgZmlsdGVyZWQuCiAgZ2F0aGVyKG1lYXN1cmUsIHZhbHVlLCBjKCJleWV0cl92YWx1ZSIsICJtb3RyX3ZhbHVlIikpICAlPiUKICAjIGZpbHRlcihtZXRyaWMgIT0gImZpcnN0X2R1cmF0aW9uIikgJT4lCiAgZmlsdGVyKG1ldHJpYyAhPSAiRlBSZWciKQoKIyBWaWV3KHByb3ZvX2RmKQoKCmBgYAoKCmBgYHtyfQpwcm92b19kZiAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgY29sb3I9bWV0cmljKSkgKwogICAgZ2VvbV9kZW5zaXR5KCkgKwogICAgZmFjZXRfd3JhcCgufm1lYXN1cmUpICsKICAgIHhsYWIoIlJlYWRpbmcgTWVhc3VyZSIpCiMgZ2dzYXZlKCIuLi92aXN1YWxpemF0aW9uL2RlbnNpdHkucG5nIiwgZGV2aWNlID0gInBuZyIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gMi41KQpgYGAKCmBgYHtyfQpwcm92b19kZiAlPiUKICBmaWx0ZXIobWVhc3VyZSA9PSAibW90cl92YWx1ZSIpICU+JQogIGdncGxvdChhZXMoeCA9IHZhbHVlLCBjb2xvcj1tZXRyaWMpKSArCiAgICBnZW9tX2RlbnNpdHkoKSArCiAgICB4bGFiKCJSZWFkaW5nIE1lYXN1cmUiKSArCiAgICBnZ3RpdGxlKCJEZW5zaXR5IHBsb3Qgb2YgdmFsdWUgZm9yIG1vdHIiKQpgYGAKCmBgYHtyfQpwcm92b19kZiAlPiUKICBmaWx0ZXIobWVhc3VyZSA9PSAiZXlldHJfdmFsdWUiKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgY29sb3I9bWV0cmljKSkgKwogICAgZ2VvbV9kZW5zaXR5KCkgKwogICAgeGxhYigiUmVhZGluZyBNZWFzdXJlIikgKwogICAgZ2d0aXRsZSgiRGVuc2l0eSBwbG90IG9mIHZhbHVlIGZvciBleWV0ciIpCmBgYAoKCmBgYHtyfQpnZF9kZiA9IHByb3ZvX2RmICU+JSBmaWx0ZXIobWV0cmljID09ICJ0b3RhbF9kdXJhdGlvbiIpICU+JSBzcHJlYWQobWVhc3VyZSwgdmFsdWUpCiMgVmlldyhnZF9kZikKCmNvci50ZXN0KGdkX2RmJGV5ZXRyX3ZhbHVlLCBnZF9kZiRtb3RyX3ZhbHVlKSAgICAgICAgICAjY29yIHJpc2UgZnJvbSAwLjUyOSB0byAwLjU5ODkgCgpwcm92b19kZiAlPiUKICBzcHJlYWQobWVhc3VyZSwgdmFsdWUpICU+JQogIGdncGxvdChhZXMoeCA9IG1vdHJfdmFsdWUsIHk9ZXlldHJfdmFsdWUsIGNvbG9yPW1ldHJpYykpICsKICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICsKICAgIGZhY2V0X3dyYXAoLn5tZXRyaWMsIHNjYWxlcz0iZnJlZSIpICsKICAgIGdlb21fc21vb3RoKCkKCiMgZ2dzYXZlKCIuLi92aXN1YWxpemF0aW9uL21ldHJpY19jb3IucG5nIiwgZGV2aWNlID0gInBuZyIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gMi41KQoKYGBgCgpgYGB7cn0KZ2RfZGZfMiA9IHByb3ZvX2RmICU+JSBmaWx0ZXIobWV0cmljID09ICJmaXJzdF9kdXJhdGlvbiIpICU+JSBzcHJlYWQobWVhc3VyZSwgdmFsdWUpCgpjb3IudGVzdChnZF9kZl8yJGV5ZXRyX3ZhbHVlLCBnZF9kZl8yJG1vdHJfdmFsdWUpCmBgYAoKYGBge3J9CmdkX2RmXzMgPSBwcm92b19kZiAlPiUgZmlsdGVyKG1ldHJpYyA9PSAiZ2F6ZV9kdXJhdGlvbiIpICU+JSBzcHJlYWQobWVhc3VyZSwgdmFsdWUpCgpjb3IudGVzdChnZF9kZl8zJGV5ZXRyX3ZhbHVlLCBnZF9kZl8zJG1vdHJfdmFsdWUpCmBgYAoKCmBgYHtyfQpnZF9kZl80ID0gcHJvdm9fZGYgJT4lIGZpbHRlcihtZXRyaWMgPT0gImdvX3Bhc3RfdGltZSIpICU+JSBzcHJlYWQobWVhc3VyZSwgdmFsdWUpCgpjb3IudGVzdChnZF9kZl80JGV5ZXRyX3ZhbHVlLCBnZF9kZl80JG1vdHJfdmFsdWUpCgpgYGAKYGBge3J9CnByb3ZvX2RmICU+JQogIGdhdGhlcih3b3JkX3Byb3AsIHdvcmRfcHJvcF92YWwsIGMoImZyZXEiLCAibGVuIiwgInN1cnAiKSkgJT4lCiAgZmlsdGVyKG1ldHJpYyA9PSAiZ2F6ZV9kdXJhdGlvbiIpICU+JQogIGdncGxvdChhZXMoeCA9IHZhbHVlLCB5PXdvcmRfcHJvcF92YWwsIGNvbG9yID0gbWVhc3VyZSkpICsKICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICsKICAgIGZhY2V0X2dyaWQod29yZF9wcm9wfm1lYXN1cmUsIHNjYWxlcz0iZnJlZSIpICsKICAgIGdlb21fc21vb3RoKCkgKwogICAgeGxhYigiUmVhZGluZyBNZWFzdXJlIikKCiMgZ2dzYXZlKCIuLi92aXN1YWxpemF0aW9uL3dvcmRfcHJvcF9jb21wcy5wbmciLCBkZXZpY2UgPSAicG5nIiwgd2lkdGggPSA2LCBoZWlnaHQgPSAzKQpgYGAKCmBgYHtyfQpwcm92b19kZiAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgeT1mcmVxLCBjb2xvcj1tZXRyaWMpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICBmYWNldF9ncmlkKG1ldHJpY35tZWFzdXJlLCBzY2FsZXM9ImZyZWUiKSArCiAgICBnZW9tX3Ntb290aCgpCmBgYAoKYGBge3J9CnByb3ZvX2RmICU+JQogIGdncGxvdChhZXMoeCA9IHZhbHVlLCB5PXN1cnAsIGNvbG9yPW1ldHJpYykpICsKICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICsKICAgIGZhY2V0X2dyaWQobWV0cmljfm1lYXN1cmUsIHNjYWxlcz0iZnJlZSIpICsKICAgIGdlb21fc21vb3RoKCkKYGBgCgoKCiMjIFNoYXBlIG9mIHN1cnByaXNhbCAvIFJUIHJlbGF0aW9uc2hpcAoKYGBge3J9CgpmaXRfZ2FtX2lubmVyID0gZnVuY3Rpb24oYm9vdHN0cmFwX3NhbXBsZSwgbWVhbl9wcmVkaWN0b3JzKSB7CiAgCiAgZGYgPSBib290c3RyYXBfc2FtcGxlJGRhdGEKICB3ZWlnaHRzID0gdGFidWxhdGUoYXMuaW50ZWdlcihib290c3RyYXBfc2FtcGxlKSwgbnJvdyhkZikpCiAgCiAgbSA9IGdhbShwc3ljaG9tZXRyaWMgfiBzKHN1cnAsIGJzID0gJ2NyJywgayA9IDYpICsgcyhwcmV2X3N1cnAsIGJzID0gJ2NyJywgayA9IDYpICsgdGUoZnJlcSwgbGVuLCBicyA9ICdjcicpICsgdGUocHJldl9mcmVxLCBwcmV2X2xlbiwgYnMgPSAnY3InKSwgZGF0YSA9IGRmLCB3ZWlnaHRzID0gd2VpZ2h0cykKICB0ZXJtc190b19wcmVkaWN0ID0gYygicyhzdXJwKSIsICJzKHByZXZfc3VycCkiKQogIAogIG5ld2RhdGEgPSBkYXRhLmZyYW1lKHN1cnA9c2VxKDAsMjAsYnk9MC4xKSwgcHJldl9zdXJwPW1lYW5fcHJlZGljdG9ycyRzdXJwLAogICAgICAgICAgICAgICAgICAgICAgIGZyZXE9bWVhbl9wcmVkaWN0b3JzJGZyZXEsIHByZXZfZnJlcT1tZWFuX3ByZWRpY3RvcnMkZnJlcSwKICAgICAgICAgICAgICAgICAgICAgICBsZW49bWVhbl9wcmVkaWN0b3JzJGZyZXEsIHByZXZfbGVuPW1lYW5fcHJlZGljdG9ycyRmcmVxKQoKICAjIFJldHVybnMgYSBtYXRyaXggTl9zYW1wbGVzICogTl90ZXJtcy4KICBwZXJfdGVybV9wcmVkaWN0aW9ucyA9IHByZWRpY3QobSwgbmV3ZGF0YT1uZXdkYXRhLCB0ZXJtcz10ZXJtc190b19wcmVkaWN0LCB0eXBlPSJ0ZXJtcyIpCgogICMgQWRkaXRpdmUgbW9kZWwgLS0gc3VtIGFjcm9zcyBwcmVkaWN0b3IgcmVzcG9uc2UgY29udHJpYnV0aW9ucyAobWF0cml4IGNvbHVtbnMpLgogIHByZWRpY3Rpb25zID0gcm93U3VtcyhwZXJfdGVybV9wcmVkaWN0aW9ucykKCiAgcmV0dXJuKG5ld2RhdGEgJT4lIG11dGF0ZSh5PXByZWRpY3Rpb25zKSkKfQoKZml0X2dhbSA9IGZ1bmN0aW9uKGRmLCBtZWFuX3ByZWRpY3RvcnMsIGFscGhhPTAuMDUpIHsKICAjIEJvb3RzdHJhcC1yZXNhbXBsZSBkYXRhCiAgYm9vdF9tb2RlbHMgPSBkZiAlPiUgYm9vdHN0cmFwcyh0aW1lcz0xMCkgJT4lIAogICAjIEZpdCBhIEdBTSBhbmQgZ2V0IHByZWRpY3Rpb25zIGZvciBlYWNoIHNhbXBsZQogICAgbXV0YXRlKHNtb290aGVkPW1hcChzcGxpdHMsIGZpdF9nYW1faW5uZXIsIG1lYW5fcHJlZGljdG9ycz1tZWFuX3ByZWRpY3RvcnMpKQogIAogICMgRXh0cmFjdCBtZWFuIGFuZCA1JSBhbmQgOTUlIHBlcmNlbnRpbGUgeS12YWx1ZXMgZm9yIGVhY2ggc3VycHJpc2FsIHZhbHVlCiAgcmVzdWx0ID0gYm9vdF9tb2RlbHMgJT4lIAogICAgdW5uZXN0KHNtb290aGVkKSAlPiUgCiAgICBkcGx5cjo6c2VsZWN0KHN1cnAsIHkpICU+JSAKICAgIGdyb3VwX2J5KHN1cnApICU+JSAKICAgICAgc3VtbWFyaXNlKHlfbG93ZXI9cXVhbnRpbGUoeSwgYWxwaGEgLyAyKSwgCiAgICAgICAgICAgICAgICB5X3VwcGVyPXF1YW50aWxlKHksIDEgLSBhbHBoYSAvIDIpLAogICAgICAgICAgICAgICAgeT1tZWFuKHkpKSAlPiUgCiAgICB1bmdyb3VwKCkKICAKICByZXR1cm4gKHJlc3VsdCkKfQoKCmBgYAoKCmBgYHtyfQoKZ2FtX21vZGVsaW5nX2RmID0gcHJvdm9fZGYgJT4lCiAgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKSAlPiUKICBtdXRhdGUobGVuID0gbmNoYXIod29yZCkpICU+JQogIGdyb3VwX2J5KG1ldHJpYywgdGV4dF9pZCkgJT4lCiAgICBhcnJhbmdlKHdvcmRfdGV4dF9pZHgpICU+JQogICAgbXV0YXRlKHByZXZfc3VycCA9IGxhZyhzdXJwKSwKICAgICAgICAgICBwcmV2X2ZyZXEgPSBsYWcoZnJlcSksCiAgICAgICAgICAgcHJldl9sZW4gPSBsYWcobGVuKSwKICAgICAgICAgICBwcmV2X2V5ZXRyX3ZhbHVlID0gbGFnKGV5ZXRyX3ZhbHVlKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGRyb3BfbmEoKSAlPiUKICByZW5hbWUocHN5Y2hvbWV0cmljID0gbW90cl92YWx1ZSkKCgpzbW9vdGhzX2RmID0gZGF0YS5mcmFtZSgpCgptZXRyaWNzID0gYygiZ2F6ZV9kdXJhdGlvbiIsICJ0b3RhbF9kdXJhdGlvbiIsICJnb19wYXN0X3RpbWUiLCAiZmlyc3RfZHVyYXRpb24iKQpmb3IgKG0gaW4gbWV0cmljcykgewogIHByaW50KHBhc3RlMCgiRml0dGluZyBtb2RlbCBmb3IgIiwgbSkpCiAgZHVtbXlfZGYgPSBnYW1fbW9kZWxpbmdfZGYgJT4lIGZpbHRlcihtZXRyaWMgPT0gbSkKICBtZWFuX3ByZWRpY3RvcnMgPSBkdW1teV9kZiAlPiUgc3VtbWFyaXNlKHN1cnAgPSBtZWFuKHN1cnApLCBsZW4gPSBtZWFuKGxlbiksIGZyZXEgPSBtZWFuKGZyZXEpKQogIHNtb290aHMgPSBkdW1teV9kZiAlPiUgZml0X2dhbSguLCBtZWFuX3ByZWRpY3RvcnMpCiAgI0ZpeCAwIHN1cnByaXNhbCA9IDAgbXMKICBnYW1fc21vb3RocyA9IHNtb290aHMgJT4lIG11dGF0ZShkZWx0YSA9IDAgLSB5WzFdLCB5PXkgKyBkZWx0YSwgeV9sb3dlcj0geV9sb3dlciArIGRlbHRhLCB5X3VwcGVyPXlfdXBwZXIgKyBkZWx0YSkKICBzbW9vdGhzX2RmID0gcmJpbmQoc21vb3Roc19kZiwgZ2FtX3Ntb290aHMgJT4lIG11dGF0ZShwc3ljaG9tZXRyaWMgPSBtKSkKfQoKYGBgCgoKYGBge3J9CgojIFN1cnByaXNhbCBjdXJ2ZXMKICBnZ3Bsb3QoKSArCiAgICAgIGdlb21fbGluZShkYXRhID0gc21vb3Roc19kZiwgYWVzKHg9c3VycCwgeT15LCBjb2xvciA9IHBzeWNob21ldHJpYyksIHNpemU9MC43KSArCiAgICAgIGdlb21fcmliYm9uKGRhdGEgPSBzbW9vdGhzX2RmLCBhZXMoeD1zdXJwLCB5bWluPXlfbG93ZXIsIHltYXg9eV91cHBlciwgZmlsbCA9IHBzeWNob21ldHJpYyksIGFscGhhPTAuMywgc2l6ZT0wLjUpICsKICAgICAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscz1jKDAsIDEwLCAyMCksIGJyZWFrcz1jKDAsIDEwLCAyMCksIG1pbm9yX2JyZWFrcyA9IE5VTEwpICsKICAgICAgZmFjZXRfd3JhcChwc3ljaG9tZXRyaWN+LikgKwogICAgICB5bGFiKCJTbG93ZG93biBkdWUgdG8gU3VycHJpc2FsIChtcykiKSArCiAgICAgIHhsYWIoIlN1cnByaXNhbCBvZiBXb3JkIikgKwogICAgICBnZ3RpdGxlKCJSZWxhdGlvbnNoaXAgYmV0d2VlbiBNb1RSIFRpbWVzIGFuZCBTdXJwcmlzYWwiKQogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKQogICkKCgoKCmBgYAoKIyBUYXJnZXRlZCBFdmFsdWF0aW9uIERhdGEKCgoKYGBge3J9Cgptb3RyX2F0dGFjaF9kZiA9IG1vdHJfZGYgJT4lCiAgZmlsdGVyKGV4cHJfaWQgPT0gIkF0dGFjaG1lbnQiKSAlPiUKICByZW5hbWUoIGl0ZW1faWQgPSBwYXJhX25yKSAlPiUKICBtdXRhdGUoaXRlbV9pZCA9IGFzLmludGVnZXIoaXRlbV9pZCkpICU+JQogIG11dGF0ZShjb25kX2lkID0gYXMuZmFjdG9yKGNvbmRfaWQpKSAlPiUKICBtdXRhdGUoY29uZF9pZCA9IGlmX2Vsc2UoY29uZF9pZCA9PSAxLCAiTm8gQ29tbWEiLCAiQ29tbWEiKSkgJT4lCiAgZmlsdGVyKG1ldHJpYyAhPSAiRlBSZWciKSAlPiUKCiAgZmlsdGVyKCEgKGl0ZW1faWQgPT0gNCAmIGNvbmRfaWQgPT0gIk5vIENvbW1hIikgKSAlPiUgIyBqdXN0IGJlY2F1c2Ugb2YgYWxpZ25tZW50IGlzc3VlcyBmb3Igbm93CiAgCiAgbXV0YXRlKGNyaXQgPSBpZl9lbHNlKHdvcmQgPT0gImFuZCIsIHdvcmRfbnIsIGFzLmludGVnZXIoMCkgKSkgJT4lCiAgZ3JvdXBfYnkoY29uZF9pZCwgaXRlbV9pZCkgJT4lCiAgICBtdXRhdGUoY3JpdCA9IHVuaXF1ZShjcml0KVsyXSkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZSh3b3JkX25yID0gd29yZF9uciAtIGNyaXQpCgoKYWdnX21vdHJfYXR0YWNoX2RmID0gbW90cl9hdHRhY2hfZGYgJT4lCiAgZmlsdGVyKHdvcmRfbnIgPj0gLTIsIHdvcmRfbnIgPCA2KSAlPiUKICBncm91cF9ieShjb25kX2lkLCB3b3JkX25yLCBtZXRyaWMpICU+JQogICAgc3VtbWFyaXNlKCBtID0gbWVhbih2YWx1ZSksCiAgICAgICAgICAgICAgIHNkID0gc3RkLmVycm9yKHZhbHVlKSwKICAgICAgICAgICAgICAgdXBwZXIgPSBtICsgMS45NiAqIHNkLAogICAgICAgICAgICAgICBsb3dlciA9IG0gLSAxLjk4ICogc2QsCiAgICAgICAgICAgICAgIG4gPSBuKCkpICU+JQogIHVuZ3JvdXAoKQoKYWdnX21vdHJfYXR0YWNoX2RmICU+JQogIGdncGxvdChhZXMoeCA9IHdvcmRfbnIsIHkgPSBtLCBjb2xvciA9IGNvbmRfaWQpKSArCiAgICBnZW9tX3JlY3QoYWVzKHhtaW4gPSAyLjUsIHhtYXggPSA1LjUsIHltaW4gPSAxMDAsIHltYXggPSA4MDApLCBmaWxsPWFscGhhKCJ3aGl0ZSIsIDApLCBjb2xvciA9ICIjNDVlZjcwIiwgbGluZXR5cGUgPSAiZG90dGVkIikgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIGdlb21fZXJyb3JiYXIoYWVzKHltYXggPSB1cHBlciwgeW1pbiA9IGxvd2VyKSwgd2lkdGggPSAwLjMpICsKICAgIGdlb21fbGluZSgpICsKICAgICNnZW9tX3RleHQoYWVzKGxhYmVsID0gd29yZCwgeSA9IGlmX2Vsc2UoY29uZF9pZCA9PSAiQ29tbWEiLCAzMDAwLCAzNTAwKSksIHNpemUgPSAyKSArCiAgICAjZmFjZXRfZ3JpZChwYXJhX25yfmNvbmRfaWQpICsKICB5bGFiKCJSZWFkaW5nIFRpbWUiKSArCiAgeGxhYigiQ29uZGl0aW9uIikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9LTI6NSwgbGFiZWxzPWMoInRoZSIsICJtYW4iLCAiYW5kIiwgImhpcyIsICJ3aWZlIiwgInJhbiIsICJhd2F5IiwgImZyb20iKSkgKwogIGZhY2V0X2dyaWQofm1ldHJpYykgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpCiAgKQoKI2dnc2F2ZSgiLi4vdmlzdWFsaXphdGlvbi9hdHRhY2htZW50LnBuZyIsIGRldmljZSA9ICJwbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDMpCgpgYGAKCmBgYHtyfQoKb3B0aW9ucyhKVUxJQV9IT01FID0gIi9BcHBsaWNhdGlvbnMvSnVsaWEtMS44LmFwcC9Db250ZW50cy9SZXNvdXJjZXMvanVsaWEvYmluLyIpCiMgbGlicmFyeShqZ2xtbSkKIyBqZ2xtbV9zZXR1cCgpCgphdHRhY2hfbG1fZGYgPSBtb3RyX2F0dGFjaF9kZiAlPiUKICBmaWx0ZXIobWV0cmljID09ICJnYXplX2R1cmF0aW9uIikgJT4lCiAgZmlsdGVyKHdvcmRfbnIgPT0gMykgJT4lCiAgbXV0YXRlKGl0ZW1faWQgPSBhcy5mYWN0b3IoaXRlbV9pZCksCiAgICAgICAgIHN1YmogPSBhcy5mYWN0b3Ioc3ViaikpCgptID0gYXR0YWNoX2xtX2RmICU+JQogIGxtZXIodmFsdWUgfiBjb25kX2lkICsgKGNvbmRfaWQgfCBpdGVtX2lkKSArIChjb25kX2lkIHwgc3ViaiksIGRhdGE9LikKCnN1bW1hcnkobSkKCmBgYAoKCgoK